package xj.graph2d.tools;

import static javax.swing.SwingConstants.LEFT;
import static javax.swing.SwingConstants.VERTICAL;

import java.awt.Point;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.geom.Point2D;
import java.util.Observer;

import xj.graph2d.BoxGroup;
import xj.graph2d.Command;
import xj.graph2d.DrawAttr;
import xj.graph2d.DrawShape;
import xj.graph2d.DrawShapeConstants;
import xj.graph2d.DrawShapeFactory;
import xj.graph2d.DrawShapeSearch;
import xj.graph2d.GraphComponentShape;
import xj.graph2d.GraphNodeBase;
import xj.graph2d.TextShape;
import xj.graph2d.command.AddGroupCommand;
import xj.graph2d.command.AddShapeCommand;
import xj.graph2d.command.TextEditCommand;
import xj.graph2d.shapes.BoxLabel;
import xj.graph2d.shapes.HorizontalCompartmentsShape;
import xj.util.gui.Point2DFloat;

public class TextTool extends SimpleDrawTool {

  public TextTool() {
    this(null, DrawShapeConstants.USE_SYSTEM_DRAW_ATTR);
  }

  public TextTool(TextShape proto) {
    this(proto, DrawShapeConstants.USE_SYSTEM_DRAW_ATTR);
  }

  public TextTool(int cloneDrawAttrMode) {
    this(null, cloneDrawAttrMode);
  }

  public TextTool(TextShape proto, int cloneDrawAttrMode) {
    super(cloneDrawAttrMode);
    this.proto = proto;
  }

  /**
   * Point to a texte shape: edit selected text shape Otherwise: add new
   * text shape
   */
  @Override
  public void mouseLButtonPressed(Observer observer, Point p, int modifiers) {
    if (debug) {
      System.out.println("TextTool.mouseLButtonPressed() p=" + p);
    }

    start(observer);
    if (view != null) {

      view.displayHelpMessage(MSGKEY_2);

      boolean inEditing = false; 
      if (shape != null) {
	postProcess();
	inEditing = true;
      }
      shape = null;

      DrawShape selected = doc.getSelectedShape();
      doc.setSelectedShape(null);
      canvas.repaintShape(selected);

      pos = view.physicalToVirtual(p);
      DrawShapeSearch.Result result = doc.search(pos);
      if (result != null && 
	  result.position != DrawShapeConstants.PointOutside) {
	DrawShape prevSelectedShape = doc.getSelectedShape();
	doc.setSelectedShape(null);
	canvas.repaintShape(prevSelectedShape, true);
	DrawShape selectedShape = result.shape;
	if (selectedShape instanceof TextShape) {
	  // edit selected text shape
	  editTextShape((TextShape) selectedShape, pos);

	  doc.setSelectedShape(shape);
	  canvas.repaintShape(shape);

	}
      }

      if (shape == null) {
	if (!inEditing) { 
	  // add new text shape
	  if (proto != null) {
	    shape = (TextShape) DrawShapeFactory.cloneDrawShape(proto, view,
								cloneDrawAttrMode);
	  } else {
	    shape = new TextShape();
	    shape.showCursor(true);
	    DrawAttr attr = view.getDrawAttr();
	    if (attr != null && cloneDrawAttrMode == CLONE_SYSTEM_DRAW_ATTR) {
	      attr = (DrawAttr) attr.clone();
	    }
	    shape.setDrawAttr(attr);
	  }
	  
	  Command cmd = new AddShapeCommand(view, doc, shape);
	  doc.executeCommand(cmd);
	  shape.setBasePosition(pos);
	  doc.setSelectedShape(shape);
	  canvas.repaintShape(shape);
	} else { 
	  if (switchTool != null) {
	    switchTool();
	    switchTool.mouseLButtonPressed(observer, p, 0);
	    switchTool.mouseLButtonReleased(observer, p, 0);
	  }
	  canvas.clearRepaintShapes();
	  canvas.repaint();
	}
      }
    }
  }

  public void editTextShape(TextShape text, Point2D pos) {
    if (debug) {
      System.out.println("TextTool.editTextShape() text=" + text);
    }
    if (text != null) {
      shape = text;
      int c = shape.findPosition(pos);
      shape.setCursor(c);
      Command cmd = new TextEditCommand(view, doc, shape);
      doc.addCommand(cmd);
    }
  }

  @Override
  public void keyPressed(Observer observer, 
			 int keyChar, 
			 int keyCode,
			 int modifiers) {
    if (debug) System.out.println("TextTool.keyPressed() keyChar=" + keyChar + "  keyCode=" + keyCode);

    if (shape != null && 
	view != null && 
	(modifiers & InputEvent.CTRL_DOWN_MASK) == 0) {
      view.displayHelpMessage(MSGKEY_2);

      if (debug) System.out.println("TextTool.keyPressed() @1 text=" + shape.getText());

      DrawShape selected = doc.getSelectedShape();
      DrawShape parent = shape.getParent();
      boolean changed = false;
      if (selected == shape) {
	if (debug) System.out.println("TextTool.keyPressed() @2");

	canvas.repaintShape(shape);

	switch (keyCode) {
	case KeyEvent.VK_LEFT:
	  if (debug) {
	    System.out.println("TextTool.keyPressed() keyCode=VK_LEFT");
	  }
	  if (!shape.cursorAtBeginning()) {
	    shape.decCursor();
	    canvas.repaintShape(shape);
	  } else {
	    if (debug) { 
	      System.out.println("TextTool.keyPressed() keyCode=VK_LEFT");
	    }
	    TextShape line = shape.getPrevLine();
	    if (line != null) {
	      if (debug) { 
		System.out.println("TextTool.keyPressed() keyCode=VK_LEFT prevLine != null");
	      }
	      line.setCursorRightEnd();
	      doc.setSelectedShape(line);
	      canvas.repaintShape(shape);
	      canvas.repaintShape(line);
	      pos = line.getPosition();
	      ;
	      shape = line;
	    }
	  }
	  break;

	case KeyEvent.VK_RIGHT:
	  if (debug) {
	    System.out.println("TextTool.keyPressed() keyCode=VK_RIGHT");
	  }
	  if (!shape.cursorAtEnd()) { 
	    shape.incCursor();
	    canvas.repaintShape(shape);
	  } else {
	    if (debug) { 
	      System.out.println("TextTool.keyPressed() keyCode=VK_RIGHT");
	    }
	    TextShape line = shape.getNextLine();
	    if (line != null) {
	      if (debug) { 
		System.out.println("TextTool.keyPressed() keyCode=VK_RIGHT nextLine != null");
	      }
	      line.setCursor(0);
	      doc.setSelectedShape(line);
	      canvas.repaintShape(shape);
	      canvas.repaintShape(line);
	      pos = line.getPosition();
	      ;
	      shape = line;
	    }
	  }
	  break;

	case KeyEvent.VK_HOME:
	  if (debug) {
	    System.out.println("TextTool.keyPressed() keyCode=VK_HOME");
	  }
	  // shape.setCursor(0);
	  shape.setCursorLeftEnd();
	  canvas.repaintShape(shape);
	  break;

	case KeyEvent.VK_END:
	  if (debug) {
	    System.out.println("TextTool.keyPressed() keyCode=VK_END");
	  }
	  shape.setCursorRightEnd();
	  canvas.repaintShape(shape);
	  break;

	case KeyEvent.VK_DELETE:
	  if (debug) {
	    System.out.println("TextTool.keyPressed() keyCode=VK_DELETE");
	  }
	  if (!supportMultiLineText || 
	      !shape.cursorAtEnd()) { 
	    shape.deleteChar();
	  } else {
	    TextShape line = shape.getNextLine();
	    if (line != null &&
		shape.isAllowMultiLine() && 
		line.isAllowMultiLine() &&
		shape.matchingPrefixSuffix(line)) { 
	      shape.merge(line);
	      parent.removeShape(line);
	      doc.shapeRemoved(line);
	      shape.setPosition(shape.getEnd1(), true);	      
	    }
	  }

	  if (parent != null) {
	    shape.adjustParentLayout();
	    DrawShape p1 = shape.getTopShape(); 
	    canvas.repaintShape(parent);
	  } else {
	    canvas.repaintShape(shape);
	  }

	  changed = true;
	  doc.shapeChanged(shape);
	  break;

	case KeyEvent.VK_BACK_SPACE:
	  if (debug) {
	    System.out.println("TextTool.keyPressed() keyCode=VK_BACK_SPACE");
	  }
	  if (!supportMultiLineText || 
	      !shape.cursorAtBeginning()) {
	    shape.backspace();
	  } else {
	    TextShape line = shape.getPrevLine();
	    if (line != null &&
		shape.isAllowMultiLine() && 
		line.isAllowMultiLine() && 
		shape.matchingPrefixSuffix(line)) { 
	      line.setCursorRightEnd();
	      line.merge(shape);
	      line.setPosition(line.getEnd1(), true);
	      doc.setSelectedShape(line);
	      parent.removeShape(shape);
	      doc.shapeRemoved(shape);
	      pos = line.getPosition();
	      
	      shape = line;
	    }
	  }
	  if (parent != null) {
	    shape.adjustParentLayout();
	    DrawShape p1 = shape.getTopShape(); 
	    canvas.repaintShape(p1);
	  } else {
	    canvas.repaintShape(shape);
	  }
	  changed = true;
	  doc.shapeChanged(shape);
	  break;

	case KeyEvent.VK_ENTER:
	  if (debug) {
	    System.out.println("TextTool.keyPressed() keyCode=VK_ENTER");
	  }

	  if (supportMultiLineText && 
	      shape.isAllowMultiLine()) {
	    // add new shape
	    Point2D tp = shape.getEnd1();
	    Point2D newPos = new Point2DFloat((float) tp.getX(), (float) tp.getY()
					      + shape.getHeight());
	    TextShape newShape = shape.split();
	    DrawAttr attr = null;
	    if (cloneDrawAttrMode == CLONE_PROTOTYPE_DRAW_ATTR) {
	      attr = shape.getDrawAttr();
	    } else if (cloneDrawAttrMode == CLONE_SYSTEM_DRAW_ATTR) {
	      attr = view.getDrawAttr();
	    }
	    if (attr != null) {
	      attr = (DrawAttr) attr.clone();
	    }
	    newShape.setDrawAttr(attr);
	    // newShape.showCursor(true);
	    shape.insertAfter(newShape);
	    newShape.setPosition(newPos, true);
	    shape.setDisplayMode(NORMAL);

	    if (parent == null) {
	      if (addMultiLineTextToBox) {
		BoxGroup box = null;
		BoxGroup proto = shape.getBoxPrototype();
		if (proto != null) {
		  try { 
		    box = (BoxGroup) proto.clone();
		  } catch (CloneNotSupportedException e) {
		    box = null;
		  }
		}
		if (box == null) { 
		  box = new BoxGroup();
		  box.setDisplayName("Text box");
		  box.setAlignment(view.getAlignment());
		}

		Command gcmd = new AddGroupCommand(view, doc, box, parent, shape);
		doc.executeCommand(gcmd);

		if (debug) {
		  System.out.println("TextTool: box count[1]=" + box.getShapeCount());
		}

		parent = box;
	      }
	    } else if (parent instanceof GraphComponentShape) {
	      BoxLabel box = new BoxLabel(VERTICAL, LEFT); //CENTER);
	      box.setRole(shape.getRole());
	      box.setDetachable(shape.isDetachable());
	      box.setScalable(shape.isScalable());
	      box.setSticky(shape.isSticky());
	      Command gcmd = new AddGroupCommand(view, doc, box, parent, shape);
	      doc.executeCommand(gcmd);
	      parent = box;
	    }

	    int ln = parent.indexOfShape(shape);
	    Command cmd = new AddShapeCommand(view, doc, parent, newShape,
					      ln + 1);
	    doc.executeCommand(cmd);

	    if (debug) {
	      System.out.println("TextTool: box count[2]=" + parent.getShapeCount());
	    }

	    if (parent != null) {
	      shape.adjustParentLayout();
	      DrawShape p1 = shape.getTopShape(); 
	      canvas.repaintShape(p1);
	    } else {
	      canvas.repaintShape(shape);
	      canvas.repaintShape(newShape);
	    }
	    shape = newShape;
	    pos = newPos;

	    changed = true;
	    doc.shapeChanged(shape);
	    doc.setSelectedShape(newShape);
	  }
	  break;

	case KeyEvent.VK_UP:
	case KeyEvent.VK_DOWN:
	  if (supportMultiLineText) {
	    TextShape line;
	    if (keyCode == KeyEvent.VK_UP) {
	      line = shape.getPrevLine();
	    } else {
	      line = shape.getNextLine();
	    }
	    if (line != null) {
	      line.setCursor(shape.getCursor());
	      doc.setSelectedShape(line);
	      canvas.repaintShape(shape);
	      canvas.repaintShape(line);
	      pos = line.getPosition();
	      ;
	      shape = line;
	    }
	  }
	  break;

	case KeyEvent.VK_F1:
	case KeyEvent.VK_F2:
	case KeyEvent.VK_F3:
	case KeyEvent.VK_F4:
	case KeyEvent.VK_F5:
	case KeyEvent.VK_F6:
	case KeyEvent.VK_F7:
	case KeyEvent.VK_F8:
	case KeyEvent.VK_F9:
	case KeyEvent.VK_F10:
	case KeyEvent.VK_F11:
	case KeyEvent.VK_F12:
	case KeyEvent.VK_F13:
	case KeyEvent.VK_F14:
	case KeyEvent.VK_F15:
	case KeyEvent.VK_F16:
	case KeyEvent.VK_F17:
	case KeyEvent.VK_F18:
	case KeyEvent.VK_F19:
	case KeyEvent.VK_F20:
	case KeyEvent.VK_F21:
	case KeyEvent.VK_F22:
	case KeyEvent.VK_F23:
	case KeyEvent.VK_F24:
	case KeyEvent.VK_INSERT:
	case KeyEvent.VK_SHIFT:
	case KeyEvent.VK_CAPS_LOCK:
	case KeyEvent.VK_ALT:
	case KeyEvent.VK_NUM_LOCK:
	case KeyEvent.VK_PRINTSCREEN:
	case KeyEvent.VK_SCROLL_LOCK:
	case KeyEvent.VK_PAUSE:
	case KeyEvent.VK_PAGE_UP:
	case KeyEvent.VK_PAGE_DOWN:
	  break;

	case KeyEvent.VK_NUMPAD0:
	case KeyEvent.VK_NUMPAD1:
	case KeyEvent.VK_NUMPAD2:
	case KeyEvent.VK_NUMPAD3:
	case KeyEvent.VK_NUMPAD4:
	case KeyEvent.VK_NUMPAD5:
	case KeyEvent.VK_NUMPAD6:
	case KeyEvent.VK_NUMPAD7:
	case KeyEvent.VK_NUMPAD8:
	case KeyEvent.VK_NUMPAD9:
	  break;

	default:
	  if (debug) {
	    System.out.println("TextTool.keyPressed() @3 default");
	  }

	  // if (!Character.isISOControl(keyCode)) {
	  if (!Character.isISOControl((char) keyChar)) {
	    // shape.append((char)keyChar);
	    shape.insert((char) keyChar);

	    if (debug) {
	      System.out.println("TextTool.keyPressed() @4 insert text=" + shape.getText() + 
				 " prompt=" + shape.getPrompt() + 
				 " (parent == null)=" + (parent == null));
	    }
	  }
	  if (parent != null) {
	    shape.adjustParentLayout();
	    DrawShape p1 = shape.getTopShape(); 
	    //System.out.println("TextTool.keyPressed() @5 repaint " + p1);
	    canvas.repaintShape(p1);
	  } else {
	    canvas.repaintShape(shape);
	  }
	  changed = true;
	  doc.shapeChanged(shape);
	}

      }

      if (changed) {
	shape.fireLabelChanged();
      }
    }
  }

  @Override
  public void mouseMoved(Observer observer, Point p, int modifiers) {
    if (view != null) {

      displayLocation(p);

      if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0) {
	// edit selected text shape
	view.displayHelpMessage(MSGKEY_3);
      } else {
	// add new text shape
	view.displayHelpMessage(MSGKEY_1);
      }
    }
  }

  /*
   * When the TextTool is deactivated and the current text shape is empty
   * remove the text shape.
   */
  @Override
  public void deactivate(Observer observer) {
    postProcess();
  }

  protected void postProcess() {
    if (shape != null && shape.getPrompt() == null && shape.length() == 0) {
      /*
       * Command cmd = new RemoveShapeCommand(view, doc, shape);
       * doc.executeCommand(cmd);
       */
      doc.revokeLastCommand();
      shape = null;
    }
  }

  public TextShape getProto() {
    return proto;
  }

  public void setProto(TextShape proto) {
    this.proto = proto;
  }

  protected Point2D pos;

  protected TextShape shape;

  protected TextShape proto;

  protected boolean supportMultiLineText = true;

  protected boolean addMultiLineTextToBox = true;

  protected static final String MSGKEY_1 = "TextTool.message.1";

  protected static final String MSGKEY_2 = "TextTool.message.2";

  protected static final String MSGKEY_3 = "TextTool.message.3";

  public static final boolean debug = false;

}
